package com.mybatis.sqlSession;

import com.mybatis.config.BoundSql;
import com.mybatis.pojo.Configuration;
import com.mybatis.pojo.MappedStatement;
import com.mybatis.utils.GenericTokenParser;
import com.mybatis.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor{
    @Override
    public <E> List<E> query(Configuration configuration,
                             MappedStatement mappedStatement,
                             Object... params) throws Exception {
        Connection connection = configuration.getDataSource().getConnection();
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        String parameterType = mappedStatement.getParameterType();
        Class<?> parameterTypeClass = getParameterTypeClass(parameterType);
        //设置参数
        List<String> parameterNames = boundSql.getParameterNames();
        for (int i = 0; i < parameterNames.size(); i++) {
            String parameterName = parameterNames.get(i);
            Field field = parameterTypeClass.getDeclaredField(parameterName);
            field.setAccessible(true);
            Object o = field.get(params[0]);
            preparedStatement.setObject(i+1,o);
        }

        ResultSet resultSet = preparedStatement.executeQuery();

        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = getParameterTypeClass(resultType);
        ArrayList<Object> objects = new ArrayList<>();
        while (resultSet.next()) {
            Object instance = resultTypeClass.newInstance();
            ResultSetMetaData metaData = resultSet.getMetaData();
            Field[] fields = instance.getClass().getDeclaredFields();
            for (int i = 1; i <=metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);
                for (Field field : fields) {
                    if (columnName.equals(field.getName())) {
                        Object object = resultSet.getObject(columnName);
                        PropertyDescriptor descriptor = new PropertyDescriptor(columnName,resultTypeClass);
                        Method writeMethod = descriptor.getWriteMethod();
                        writeMethod.invoke(instance,object);
                    }
                }
            }
            objects.add(instance);
        }
        return (List<E>) objects;
    }

    private Class<?> getParameterTypeClass(String parameterType) throws ClassNotFoundException {
        if(parameterType!=null){
            Class<?> parameterTypeClass = Class.forName(parameterType);
            return parameterTypeClass;
        }
        return null;
    }

    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser tokenParser = new GenericTokenParser("#{","}",tokenHandler);
        String sqlText = tokenParser.parse(sql);
        List<String> parameterNames = tokenHandler.getParameterNames();
        return new BoundSql(sqlText,parameterNames);
    }
}
